package com.ztrue.framework.resources;

import com.ztrue.framework.core.command.ICommandHandler;
import com.ztrue.framework.core.domain.AggregateRoot;
import com.ztrue.framework.core.event.IDomainEvent;
import com.ztrue.framework.core.event.IDomainEventHandler;
import com.ztrue.framework.core.annotation.running.SyncHandler;
import com.ztrue.framework.handler.IHandlerDefinitionRegistry;
import com.ztrue.framework.handler.definition.*;
import com.ztrue.common.log.Logger;
import com.ztrue.common.scan.ClassScaner;

import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;

/**
 * 处理器扫描器
 *
 * @author zhangw
 * @date 2016/11/10 15:46
 * @version: 1.0
 */
public class ClassPathHandlerDefinitionScanner {
    private static final Logger logger = Logger.getLogger(ClassPathHandlerDefinitionScanner.class);
    /**
     * 类加载器
     */
    private ClassLoader classLoader;
    /**
     * 处理器定义注册表
     */
    private IHandlerDefinitionRegistry handlerDefinitionRegistry;

    /**
     * 初始化处理器扫描器，使用当前线程上下文类加载器
     *
     * @param handlerDefinitionRegistry 处理器定义注册
     */
    public ClassPathHandlerDefinitionScanner(IHandlerDefinitionRegistry handlerDefinitionRegistry) {
        this(Thread.currentThread().getContextClassLoader(), handlerDefinitionRegistry);
    }

    /**
     * 初始化处器扫描器，使用指定类加载器
     *
     * @param classLoader               指定的类加载器
     * @param handlerDefinitionRegistry 处理器定义注册
     */
    public ClassPathHandlerDefinitionScanner(ClassLoader classLoader, IHandlerDefinitionRegistry handlerDefinitionRegistry) {
        this.classLoader = classLoader;
        this.handlerDefinitionRegistry = handlerDefinitionRegistry;
    }

    /**
     * 扫描指定包路径
     *
     * @param basePackages
     * @return 成功扫描数量
     */
    public int scan(String... basePackages) {
        return doScan(basePackages).size();
    }

    /**
     * 扫描指定包路径
     *
     * @param basePackages
     * @return 处理器定义集合
     */
    protected Set<IHandlerDefinition> doScan(String... basePackages) {
        try {
            Set<IHandlerDefinition> handlerDefinitions = new LinkedHashSet<>();
            for (String basePackage : basePackages) {
                handlerDefinitions.addAll(findComponents(basePackage));
            }
            return handlerDefinitions;
        } catch (IOException e) {
            logger.error("读取资源时发生异常", e);
            throw new ResourcesLoadException("资源加载异常", e);
        }
    }

    /**
     * 查找指定包路径内的处理器
     *
     * @param basePackage
     * @return 处理器定义集合
     */
    private Set<IHandlerDefinition> findComponents(String basePackage) throws IOException {
        Set<IHandlerDefinition> handlerDefinitions = new LinkedHashSet<>();
        ClassScaner classScanner = new ClassScaner(this.classLoader);
        Set<String> classNames = classScanner.getClassnameFromPackage(basePackage, true);
        Class cls;
        Set<IHandlerDefinition> handlerDefinitionSet;
        for (String className : classNames) {
            try {
                cls = Class.forName(className, false, this.classLoader);
                handlerDefinitionSet = doDefinition(cls);
                if (handlerDefinitionSet != null && handlerDefinitionSet.size() > 0) {
                    handlerDefinitions.addAll(handlerDefinitionSet);
                }
            } catch (ClassNotFoundException e) {
                logger.warn("无法加载类：" + className, e);
                throw new ResourcesLoadException("无法加载类：" + className, e);
            }
        }
        return handlerDefinitions;
    }

    /**
     * 通过Class创建定义
     *
     * @param cls
     * @return
     */
    private Set<IHandlerDefinition> doDefinition(Class cls) {
        Set<IHandlerDefinition> handlerDefinitionSet = new LinkedHashSet<>();
        IHandlerDefinition handlerDefinition;
        try {
            if (AggregateRoot.class.isAssignableFrom(cls)) {
                handlerDefinitionSet.addAll(doMethodLoader(cls));
            } else {
                handlerDefinition = doClassLoader(cls);
                if (handlerDefinition != null)
                    handlerDefinitionSet.add(handlerDefinition);
            }
        } catch (ResourcesLoadException e) {
            logger.warn("无法加载处理器[" + cls.getName() + "]目标类型", e);
            throw new ResourcesLoadException("无法加载处理器[" + cls.getName() + "]目标类型", e);
        }
        return handlerDefinitionSet;
    }

    /**
     * 处理类加载器
     *
     * @param cls
     * @return
     * @throws ClassNotFoundException
     */
    private IHandlerDefinition doClassLoader(Class cls) throws ResourcesLoadException {
        try {
            AbstractHandlerDefinition handlerDefinition = null;
            if (ICommandHandler.class.isAssignableFrom(cls)) {
                handlerDefinition = new CommandHandlerDefinition();
            } else if (IDomainEventHandler.class.isAssignableFrom(cls)) {
                handlerDefinition = new EventHandlerDefinition();
            }
            if (handlerDefinition != null) {
                if (cls.getGenericSuperclass() != null && cls.getGenericSuperclass() != Object.class) {
                    Type[] actuals = ((ParameterizedType) cls.getGenericSuperclass()).getActualTypeArguments();
                    if (actuals != null || actuals.length > 0) {
                        Class targetClass = Class.forName(actuals[0].getTypeName(), false, this.classLoader);
                        if (handlerDefinition != null) {
                            handlerDefinition.setAsync(!cls.isAnnotationPresent(SyncHandler.class));
                            handlerDefinition.setHandlerClass(cls);
                            handlerDefinition.setHandlerTargetClass(targetClass);
                        }
                        this.handlerDefinitionRegistry.registerHandlerDefinition(handlerDefinition.getHandlerClassName(), handlerDefinition);
                    }
                }
            }
            return handlerDefinition;
        } catch (ClassNotFoundException e) {
            throw new ResourcesLoadException("资源加载异常，无法加载类[" + cls.getName() + "]", e);
        }
    }

    /**
     * 处理方法加载器
     * 扫描类中的方法，匹配参数为IDomainEvent子类的方法
     *
     * @param cls
     * @return
     */
    private Set<IHandlerDefinition> doMethodLoader(Class cls) {
        Set<IHandlerDefinition> handlerDefinitionSet = new LinkedHashSet<>();
        EventHandlerMethodDefinition handlerDefinition;
        for (Method method : cls.getDeclaredMethods()) {
            Class[] clss = method.getParameterTypes();
            if (clss != null && clss.length == 1 && IDomainEvent.class.isAssignableFrom(clss[0])) {
                handlerDefinition = new EventHandlerMethodDefinition();
                handlerDefinition.setExecMethod(method);
                handlerDefinition.setAsync(!method.isAnnotationPresent(SyncHandler.class));
                handlerDefinition.setHandlerClass(cls);
                handlerDefinition.setHandlerTargetClass(clss[0]);
                handlerDefinitionSet.add(handlerDefinition);
                this.handlerDefinitionRegistry.registerHandlerDefinition(handlerDefinition.getHandlerClassName() + "-" + method.getName(), handlerDefinition);
            }
        }
        return handlerDefinitionSet;
    }
}